
#This file is part of lllm.

#    lllm is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

#    lllm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

#    You should have received a copy of the GNU Affero General Public License along with lllm. If not, see <https://www.gnu.org/licenses/>. 

#!/bin/janet
(import "./lllm")

(def [*assembler-backend* #Config:
      *linker-backend*
      *version*]
  [["/bin/nasm" "-felf64"]
   ["/bin/ld"]
   "git-0.0.5" #TODO: Figure out how to get the version from project.janet .
  ])

(defn fileread (F) #Small util to read a file.
  (def f (file/open F :r))
  (def f- (file/read f :all))
  (file/close f)
  f-
  )
(defn filewrite (F str) # Small util to write a file.
  (def f (file/open F :w))
  (file/close (file/write f str))
   F)
(defn help ()
 (print "
usage: lllma Command args... \n
 Commands:\n
    help        : Show this. llllm has lots of comments as documentation to\n
help you use lllm.\n
    build F O  : Build file F(.janet?) , which produces\n
O.jimage , O.asm , O.o and F (binary executable) .\n
    meta  F O  : Metassemble file F(.janet?) , which produces only\n
O.jimage .\n
    trans F O  : Transsemble file F(.janet?) , which produces only\n
O.asm .\n
    asm   F O  : Assemble file F(.janet?) , which produces only\n
O.o .\n
    link  F O  : Link file F(.janet?) , which produces only\n
O (binary executable) .\n
    run   F O  : Equivalent to llllma build F O then running O. Produces\n
O.jimage , O.asm , O.o and F (binary executable) .\n
    eval  F    : Runs the executable binary F metassembles to.\n
No files produced*. (*: Uses /tmp/janet-lllm as temporary storage.)\n"))
(defn meta (F &opt O &)
  (default O "./")
  (def F.meta (string/replace ".janet" ".jimage" F ))
  (print "Metassembling " F " to " (string O F.meta))
   (filewrite (string O F.meta) (make-image
                                 (eval-string (fileread F)))))
  #=>1| @[
  #  2|[op arg1 arg2]
  #  3|[op2 arg3 arg4]
  #  ...
  #  9| ]
(defn trans (F &opt O &)
  (default O "./")
  (def F- (string/replace ".jimage" ".janet" F))
  (def F.trans (string/replace ".janet" ".asm" F-))
  (print "Transsembling " F " to " (string O F.trans))
  (filewrite (string O F.trans) (lllm/lllm
                                (eval-string (fileread F)))))
#-> F.asm (assembly file)
(defn asm (F &opt O &)
  (default O "./")
  (def F- (string/replace ".jimage" ".janet" F))
  (def F.asm (string/replace ".janet" "-at.asm" F-))
  (def F.o (string/replace ".janet" ".o" F-))
  (print "Assembling " F " to " (string O F.o))
  (filewrite (string O F.asm) (lllm/lllm
                               (eval-string (fileread F))))
  (os/execute [;*assembler-backend* (string O F.asm) "-o" F.asm ])
  (os/execute ["/bin/rm" (string O F.asm)])
  (string O F.asm)) #Standardized filewrite behavior imitation.
(defn link (F &opt O &)
  (print (os/cwd))
  (default O "./")
  (def F- (string/replace ".jimage" ".janet" F))
  (def F.asm (string/replace ".janet" "-lt.asm" F-))
  (def F.o (string/replace ".janet" "-lt.o" F-))
  (def F.exe  (string/replace ".janet" "" F-))
  (print "Linking " F " to " (string O F.exe))
  (filewrite (string O F.asm) (lllm/lllm
                               (eval-string (fileread F))))
  (os/execute [;*assembler-backend* (string O F.asm)])
  (os/execute [;*linker-backend* (string O F.o) "-o" (string O F.exe)])
  #Cleanup:
  (os/execute ["/bin/rm" (string O F.asm)])
  (os/execute ["/bin/rm" (string O F.o)])
  (string O F.exe)) #Useful for build and run below.
(defn build (F &opt O &)
  (print F O)
  (default O "./")
  (def F- (string/replace F ".janet" ".jimage") )
  (meta F O)
  (trans F- O)
  (asm F O)
  (link F O)
  )
(defn run (F &opt O &)
  (default O "./")
  (os/execute [(build F O)])
  )
(defn evalllm (F &opt O &) #args)
  (default O "./")
  (def F.asm (string/replace ".janet" ".asm" F))
  (def F.o (string/replace ".janet" ".o" F))
  (def F.exe  (string/replace ".janet" "" F))
  (print "Running " F " (in tmp environment " (string O F.exe) " )" )
  (filewrite (string O F.asm) (lllm/lllm
                               (eval-string (fileread F))))
  (os/execute [;*assembler-backend* (string O F.asm)])
  (os/execute [;*linker-backend* (string O F.o) "-o" (string O F.exe)])
  #Cleanup:
  (os/execute ["/bin/rm" (string O F.asm)])
  (os/execute ["/bin/rm" (string O F.o)])
  #Run:
  (os/execute [(string O F.exe) ])
  #Resume and finish cleanup:
  (os/execute ["/bin/rm" (string O F.exe)])
  (string O F.exe)) #Std filewrite beh imit
(defn ver (&) (print "lllm-" *version*))

(def cmds {"h" help
           "m" meta
           "t" trans
           "a" asm
           "l" link
           "c" link #compile
           "b" build
           "r" run
           "e" evalllm
           "v" ver
           })


(defn main (self &opt cmd & args)
  (default cmd "help")
  (if (not (cmds (string/slice cmd 0 1)))  #Is a command (first letter)
    ((fn ()
       (print (string "lllm: unknown command: " cmd " " ;(interpose " " args)))
       (help)) )
    ((cmds (string/slice cmd 0 1)) ;args)
        ))
